
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h>
#include<linux/uaccess.h>
#include <linux/err.h>

#define mem_size  1024

dev_t dev = 0;
static struct class *dev_class;
static struct cdev my_cdev;
uint8_t *data_buf;

static int my_open(struct inode *inode, struct file *file)
{
  if((data_buf = kmalloc(mem_size , GFP_KERNEL)) == 0){
    pr_info("Failed to allocate memory for data_buf\n");
    return -1;
  }
  strcpy(data_buf, "Hello_World");
  pr_info("Device File Opened\n");
  return 0;
}

static int my_release(struct inode *inode, struct file *file)
{
  kfree(data_buf);
  pr_info("Device File Closed\n");
  return 0;
}

static ssize_t my_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
  if( copy_to_user(buf, data_buf, mem_size) )
    pr_err("Data Read : Err!\n");
  else
    pr_info("Data Read : Done!\n");
  return mem_size;
}

static ssize_t my_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
  if( copy_from_user(data_buf, buf, len) )
    pr_err("Data Write Error\n");
  else
    pr_info("Data Write Done\n");
  return len;
}

static struct file_operations fops =
{
  .owner          = THIS_MODULE,
  .read           = my_read,
  .write          = my_write,
  .open           = my_open,
  .release        = my_release,
};

static int __init my_init(void)
{
  if((alloc_chrdev_region(&dev, 0, 1, "my_dev")) <0){
    pr_info("Failed to allocate major number\n");
    return -1;
  }
  pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));

  cdev_init(&my_cdev,&fops);

  if((cdev_add(&my_cdev,dev,1)) < 0){
    pr_info("Failed to add the device to the system\n");
    goto r_class;
  }

  if(IS_ERR(dev_class = class_create("my_class"))){
    pr_info("Failed to create the struct class\n");
    goto r_class;
  }

  if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"my_device"))){
    pr_info("Failed to create the Device\n");
    goto r_device;
  }

  pr_info("Device Driver Inserted\n");
  return 0;

r_device:
  class_destroy(dev_class);
r_class:
  unregister_chrdev_region(dev,1);
  return -1;
}

static void __exit my_exit(void)
{
  device_destroy(dev_class, dev);
  class_destroy(dev_class);
  cdev_del(&my_cdev);
  unregister_chrdev_region(dev, 1);
  pr_info("Device Driver Removed\n");
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("feifei <feifei@gmail.com>");
MODULE_DESCRIPTION("Simple Linux device driver");
MODULE_VERSION("1.4");